// 泛型的作用：  在定义类型时，无法确定类型，使用时才能确定类型
// 一般用大写字面表示
class Mouse {
  constructor(public name: string, public age: number) {}
}
class Dog {
  constructor(public name: string, public age: number) {}
}
// type IClazz = new (name: string, age: number) => any;
function getInstance<T>(
  clazz: new (name: string, age: number) => T,
  name: string,
  age: number
) {
  return new clazz(name, age);
}

let r = getInstance(Mouse, "jy", 13);
r.name;

export {};

//------- 与接口联用---------

interface ICreateArray {
  <T>(num: number, val: T): T[];
}

type ICreateArray1 = <T>(num: number, val: T) => T[];
const createArray: ICreateArray = (num, val) => {
  let res = [];
  for (let i = 0; i < num; i++) {
    res.push(val);
  }
  return res;
};

let arr = createArray(5, "abc");

//  区分接口泛型  泛型变量传递
/**
 * 接口后面+ 泛型 （在使用接口之前就可以确定类型）  callback: ICallBack<T>
 * 函数签名+泛型   （在调用函数时才能确定） <T>(item: T): void;
 */

interface ICallBack<T> {
  (item: T): void;
}
type IForEach = <T>(arr: T[], callback: ICallBack<T>) => void;
let forEach: IForEach = (arr, callback) => {
  for (let i = 0; i < arr.length; i++) {
    callback(arr[i]);
  }
};

forEach([1, "string"], (item) => {
  console.log(item);
});

//  交换参数 (多个泛型，依靠位置进行推导)
function swap<T, K>(tuple: [T, K]): [K, T] {
  return [tuple[1], tuple[0]];
}
let s = swap([1, "a"]);

//  计算后无法推导类型   无法保证 T+T = T
/**
function sum<T>(a: T, b: T): T {
  return a + b;
}
sum("1", "2");
 */

//  泛型约束
// function sum<T extends string>(a: T, b: T) :T{
//.. 即使约束了类型，泛型依旧认定 T+T !== T
function sum<T extends string>(a: T, b: T) {
  return a + b;
}
sum("1", "2");

// 满足条件 ===  只需要涵盖定义的属性
function getVal<T extends { toString: () => string }>(v: T) {}
getVal(1);

type IA = { a: 1 };
type IB = { b: 1 };
function demo<T extends IA | IB>(obj: T) {}
demo({ a: 1, b: 2 });
demo({ a: 1, b: 2, c: 3 });
// demo({c:3}) error

// keyof关键字  取对象中key 只能对类型使用

let obj = { name: 1, age: 1 };

function getValueByObj<T extends object, R extends keyof T>(target: T, a: R) {
  // 约束key必须在target中包含
}
getValueByObj(obj, "name");
// getValueByObj(obj,'name1')  error
type n = keyof never; // string | number | symbol
type a = keyof any; // string | number | symbol
interface Fn {
  (a: string): void;
}
type f = keyof Fn; // never
